﻿// --- Copyright (c) 2010 Steven Hartgers ---
// Copyright (C) 2010 Steven Hartgers All rights are reserved.
// Author contact: fiinix00/at\gmail.com
// ------------------------------------------------------------------------
// This program is free software: you can redistribute it and/or modify 
// it under the terms of the GNU General Public License as published by 
// the Free Software Foundation, either version 3 of the License, or 
// (at your option) any later version. 
//  
// This program is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
// GNU General Public License for more detail
//  
// You should have received a copy of the GNU General Public License 
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
// ------------------------------------------------------------------------
// COPYRIGHT NOTICE: This copyright notice may NOT be removed, obscured or modified 
// without written consent from the author.
// --- End copyright notice --- 


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Hack.Numbers
{
        public class BigNumber
        {
            public UInt32[] Numbers { get { return this.number; } }
            private UInt32[] number;
            private int size;
            private int maxDigits;

            public BigNumber(int maxDigits)
            {
                this.maxDigits = maxDigits;
                this.size = (int)System.Math.Ceiling((float)maxDigits * 0.104) + 2;
                number = new UInt32[size];
            }
            public BigNumber(int maxDigits, UInt32 intPart)
                : this(maxDigits)
            {
                number[0] = intPart;
                for (int i = 1; i < size; i++)
                {
                    number[i] = 0;
                }
            }
            private void VerifySameSize(BigNumber value)
            {
                if (Object.ReferenceEquals(this, value))
                    throw new Exception("BigNumbers cannot operate on themselves");
                if (value.size != this.size)
                    throw new Exception("BigNumbers must have the same size");
            }

            public void Add(BigNumber value)
            {
                VerifySameSize(value);

                int index = size - 1;
                while (index >= 0 && value.number[index] == 0)
                    index--;

                UInt32 carry = 0;
                while (index >= 0)
                {
                    UInt64 result = (UInt64)number[index] +
                                    value.number[index] + carry;
                    number[index] = (UInt32)result;
                    if (result >= 0x100000000U)
                        carry = 1;
                    else
                        carry = 0;
                    index--;
                }
            }
            public void Subtract(BigNumber value)
            {
                VerifySameSize(value);

                int index = size - 1;
                while (index >= 0 && value.number[index] == 0)
                    index--;

                UInt32 borrow = 0;
                while (index >= 0)
                {
                    UInt64 result = 0x100000000U + (UInt64)number[index] -
                                    value.number[index] - borrow;
                    number[index] = (UInt32)result;
                    if (result >= 0x100000000U)
                        borrow = 0;
                    else
                        borrow = 1;
                    index--;
                }
            }
            public void Multiply(UInt32 value)
            {
                int index = size - 1;
                while (index >= 0 && number[index] == 0)
                    index--;

                UInt32 carry = 0;
                while (index >= 0)
                {
                    UInt64 result = (UInt64)number[index] * value + carry;
                    number[index] = (UInt32)result;
                    carry = (UInt32)(result >> 32);
                    index--;
                }
            }
            public void Divide(UInt32 value)
            {
                int index = 0;
                while (index < size && number[index] == 0)
                    index++;

                UInt32 carry = 0;
                while (index < size)
                {
                    UInt64 result = number[index] + ((UInt64)carry << 32);
                    number[index] = (UInt32)(result / (UInt64)value);
                    carry = (UInt32)(result % (UInt64)value);
                    index++;
                }
            }
            public void Assign(BigNumber value)
            {
                VerifySameSize(value);
                for (int i = 0; i < size; i++)
                {
                    number[i] = value.number[i];
                }
            }

            public string Print()
            {
                BigNumber temp = new BigNumber(maxDigits);
                temp.Assign(this);

                StringBuilder sb = new StringBuilder();
                sb.Append(temp.number[0]);
                sb.Append(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalSeparator);

                int digitCount = 0;
                while (digitCount < maxDigits)
                {
                    temp.number[0] = 0;
                    temp.Multiply(100000);
                    sb.AppendFormat("{0:D5}", temp.number[0]);
                    digitCount += 5;
                }

                return sb.ToString();
            }
            public bool IsZero()
            {
                foreach (UInt32 item in number)
                {
                    if (item != 0)
                        return false;
                }
                return true;
            }

            public void ArcTan(UInt32 multiplicand, UInt32 reciprocal)
            {
                BigNumber X = new BigNumber(maxDigits, multiplicand);
                X.Divide(reciprocal);
                reciprocal *= reciprocal;

                this.Assign(X);

                BigNumber term = new BigNumber(maxDigits);
                UInt32 divisor = 1;
                bool subtractTerm = true;
                while (true)
                {
                    X.Divide(reciprocal);
                    term.Assign(X);
                    divisor += 2;
                    term.Divide(divisor);
                    if (term.IsZero())
                        break;

                    if (subtractTerm)
                        this.Subtract(term);
                    else
                        this.Add(term);
                    subtractTerm = !subtractTerm;
                }
            }

        }
        public static class PI
        {
            public static BigNumber CalculatePI(int digits)
            {
                BigNumber x = new BigNumber(digits);
                BigNumber y = new BigNumber(digits);
                x.ArcTan(16, 5);
                System.Console.WriteLine("ArcTan(16, 5): Done!");
                y.ArcTan(4, 239);
                System.Console.WriteLine("ArcTan(4, 239): Done!");
                x.Subtract(y);
                System.Console.WriteLine("Subtract(locals:y\\" + y.GetType().Name + "): Done!");
                return x;
            }
            public const Double DefaultPI = System.Math.PI;
        }
        public static class ByteConversion
        {
 
        }
}